package com.lb.uts.client.annotation;

import com.lb.uts.client.model.UtsClientConstants;
import org.apache.ibatis.builder.StaticSqlSource;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Map;
import java.util.Properties;

/**
 * 只能针对mysql，如果需要满足其他数据库（比如oracle）需要实现自己的分页处理
 * 批次执行Interceptor
 * 1、查询出全部的数量
 * 2、根据执行器数量和自己执行顺序进行分页处理
 */
@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,
                RowBounds.class, ResultHandler.class})})
public class BatchInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Map<String, Object> batchMap = UtsClientConstants.get();
        if (batchMap != null) {
            Integer batchPageNo = Integer.valueOf(batchMap.get(UtsClientConstants.BATCH_PAGE_NO).toString());
            MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
            Configuration configuration = mappedStatement.getConfiguration();
            Object parameter = null;
            if (invocation.getArgs().length > 1) {
                parameter = invocation.getArgs()[1];
            }
            BoundSql boundSql = mappedStatement.getBoundSql(parameter);
            String namespace = mappedStatement.getId();
            String className = namespace.substring(0, namespace.lastIndexOf("."));
            String methedName = namespace.substring(namespace.lastIndexOf(".") + 1, namespace.length());
            Method[] ms = Class.forName(className).getMethods();
            Annotation annotation = null;
            for (Method m : ms) {
                if (m.getName().equals(methedName)) {
                    annotation = m.getAnnotation(Batch.class);
                    break;
                }
                ;
            }
            if (annotation == null) {
                return invocation.proceed();
            }
            String originalSql = boundSql.getSql().trim();
            originalSql = originalSql.toLowerCase();
            String countSql = "select count(1) " + originalSql.substring(originalSql.indexOf("from"));
            Connection connection = mappedStatement.getConfiguration().getEnvironment().getDataSource().getConnection();
            PreparedStatement countStmt = connection.prepareStatement(countSql);
            BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql,
                    boundSql.getParameterMappings(), boundSql.getParameterObject());
            DefaultParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement,
                    boundSql.getParameterObject(), countBS);
            parameterHandler.setParameters(countStmt);
            ResultSet rs = countStmt.executeQuery();
            int totalNum = 0;
            if (rs.next()) {
                totalNum = rs.getInt(1);
            }
            rs.close();
            countStmt.close();
            connection.close();
            UtsClientConstants.remove();
            //分页处理（这里可以抽成一个方法，用于支持不同的数据库）
            Integer batchPageTotal = Integer.valueOf(batchMap.get(UtsClientConstants.BATCH_PAGE_TOTAL).toString());
            int num = totalNum % batchPageTotal == 0 ? totalNum / batchPageTotal : totalNum / batchPageTotal + 1;
            if (totalNum < UtsTaskProperties.batchMinLimit) {
                num =  UtsTaskProperties.batchMinLimit;
            }
            String pageSql = originalSql + " limit " + (batchPageNo - 1) * num + "," + batchPageNo * num;
            ReflectUtil.setFieldValue(boundSql, "sql", pageSql);
            StaticSqlSource sqlsource = new StaticSqlSource(configuration, pageSql,
                    boundSql.getParameterMappings());
            MappedStatement.Builder builder = new MappedStatement.Builder(configuration, "id_temp_result", sqlsource,
                    SqlCommandType.SELECT);
            builder.resultMaps(mappedStatement.getResultMaps()).resultSetType(mappedStatement.getResultSetType())
                    .statementType(mappedStatement.getStatementType());
            MappedStatement query_statement = builder.build();
            invocation.getArgs()[0] = query_statement;
            return invocation.proceed();
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

    private static class ReflectUtil {
        /**
         * 利用反射获取指定对象的指定属性
         */
        public static Object getFieldValue(Object obj, String fieldName) {
            Object result = null;
            final Field field = ReflectUtil.getField(obj, fieldName);
            if (field != null) {
                field.setAccessible(true);
                try {
                    result = field.get(obj);
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            return result;
        }

        /**
         * 利用反射获取指定对象里面的指定属性
         */
        private static Field getField(Object obj, String fieldName) {
            Field field = null;
            for (Class<?> clazz = obj.getClass(); clazz != Object.class; clazz = clazz.getSuperclass()) {
                try {
                    field = clazz.getDeclaredField(fieldName);
                    break;
                } catch (NoSuchFieldException e) {
                    // 这里不用做处理，子类没有该字段可能对应的父类有，都没有就返回null。
                }
            }
            return field;
        }

        public static void setFieldValue(Object obj, String fieldName, Object fieldValue) {
            final Field field = ReflectUtil.getField(obj, fieldName);
            if (field != null) {
                try {
                    field.setAccessible(true);
                    field.set(obj, fieldValue);
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
